/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2013, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gses.webui.osgi;
import com.google.common.base.Objects;
import org.osgi.framework.BundleException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import ru.codeinside.gses.liquibase.api.MigrationService;
import ru.codeinside.gses.migrations.Databases;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
final public class Renovation {
final Logger logger = Logger.getLogger(getClass().getName());
public void validateResources() throws BundleException {
try {
final Set<String> catalogs = new HashSet<String>();
String tmpCatalog = getCatalog(Databases.TMP);
for (final String dbName : Databases.ALL) {
String catalog = getCatalog(dbName);
catalogs.add(catalog);
if (catalogs.size() > 1) {
final String msg = dbName + " use other catalog!";
throw new BundleException(msg, BundleException.ACTIVATOR_ERROR);
}
if (Objects.equal(tmpCatalog, catalog)) {
final String msg = dbName + " and " + Databases.TMP + " use one catalog!";
throw new BundleException(msg, BundleException.ACTIVATOR_ERROR);
}
}
} catch (NamingException e) {
final Throwable cause = e.getCause();
final String msg = cause == null ? e.getMessage() : cause.getMessage();
throw new BundleException(msg, BundleException.ACTIVATOR_ERROR, e);
} catch (SQLException e) {
throw new BundleException("Database fail", BundleException.ACTIVATOR_ERROR, e);
}
}
private String getCatalog(String dbName) throws NamingException, SQLException, BundleException {
final DataSource dataSource = InitialContext.doLookup(dbName);
try {
if (!dataSource.isWrapperFor(XADataSource.class)) {
throw new BundleException(dbName + " is not XADataSource!", BundleException.ACTIVATOR_ERROR);
}
} catch (SQLFeatureNotSupportedException e) {
throw new BundleException(dbName + " is not XADataSource!", BundleException.ACTIVATOR_ERROR);
}
final Connection connection = dataSource.getConnection();
try {
return connection.getCatalog();
} finally {
connection.close();
}
}
public void validatePersistence() throws BundleException {
final InputStream is = getClass().getClassLoader().getResourceAsStream("META-INF/persistence.xml");
if (is == null) {
throw new BundleException("Missed META-INF/persistence.xml", BundleException.ACTIVATOR_ERROR);
}
try {
final DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
final Document doc = builder.parse(is);
final NodeList units = doc.getElementsByTagName("persistence-unit");
for (int i = 0; i < units.getLength(); i++) {
final Element unit = (Element) units.item(i);
if (!"JTA".equals(unit.getAttribute("transaction-type"))) {
final String msg = "JPA unit " + unit.getAttribute("name") + " without JTA";
throw new BundleException(msg, BundleException.ACTIVATOR_ERROR);
}
final NodeList propertiesList = unit.getElementsByTagName("properties");
for (int j = 0; j < propertiesList.getLength(); j++) {
final Element properties = (Element) propertiesList.item(j);
final NodeList propertyList = properties.getElementsByTagName("property");
for (int k = 0; k < propertyList.getLength(); k++) {
final Element property = (Element) propertyList.item(k);
if ("eclipselink.ddl-generation".equals(property.getAttribute("name"))) {
if (!"none".equals(property.getAttribute("value"))) {
final String msg = "JPA unit " + unit.getAttribute("name") + " with DDL generation!";
throw new BundleException(msg, BundleException.ACTIVATOR_ERROR);
}
}
}
}
}
} catch (ParserConfigurationException e) {
throw new BundleException("DOM Parser error", BundleException.ACTIVATOR_ERROR, e);
} catch (SAXException e) {
throw new BundleException("SAX error", BundleException.ACTIVATOR_ERROR, e);
} catch (IOException e) {
throw new BundleException("IO error", BundleException.ACTIVATOR_ERROR, e);
}
}
//TODO: переместить в тест
public void renovate(final MigrationService migrationService) throws Exception {
final UserTransaction tx = InitialContext.doLookup("UserTransaction");
logger.info("tx status : " + tx.getStatus());
if (tx.getStatus() != Status.STATUS_NO_TRANSACTION) {
logger.info("Rollback transaction " + tx);
tx.rollback();
}
tx.begin();
try {
renovateUnderTx(migrationService);
} catch (Exception e) {
tx.rollback();
throw e;
}
tx.commit();
}
public void renovateUnderTx(MigrationService migrationService) throws NamingException {
final DataSource tmp = InitialContext.doLookup(Databases.TMP);
for (final Map.Entry<String, String> entry : Databases.VERSIONS.entrySet()) {
final String jndiName = entry.getKey();
final String version = entry.getValue();
final String simpleName = jndiName.substring(jndiName.indexOf('/') + 1);
logger.info("Migrate " + simpleName + " to " + version);
final DataSource ds = InitialContext.doLookup(jndiName);
final String changeLog = "migrations/" + simpleName + "/migration.xml";
migrationService.migrate(changeLog, getClass().getClassLoader(), version, tmp, ds);
}
}
}